/**
 * 音频错误处理器
 * 处理Web Audio API错误、音频文件加载失败等音频相关问题
 * 提供静默模式和降级方案确保游戏在音频失败时仍能正常运行
 * @class
 */

export interface AudioFallbackOptions {
  /** 是否启用静默模式降级 */
  enableSilentMode?: boolean
  /** 是否使用HTML5 Audio作为降级方案 */
  enableHTML5AudioFallback?: boolean
  /** 是否显示音频错误警告 */
  showAudioWarning?: boolean
  /** 音频重试次数 */
  maxRetryAttempts?: number
  /** 重试间隔（毫秒） */
  retryInterval?: number
}

export interface AudioErrorInfo {
  /** 错误类型 */
  type: 'context' | 'loading' | 'playback' | 'decoding' | 'permission'
  /** 错误消息 */
  message: string
  /** 错误对象 */
  error: Error
  /** 发生错误的时间戳 */
  timestamp: number
  /** 错误发生的上下文信息 */
  context?: {
    soundName?: string
    url?: string
    audioContextState?: string
  }
}

/**
 * 音频错误处理器类
 */
export class AudioErrorHandler {
  private options: Required<AudioFallbackOptions>
  private isInSilentMode: boolean = false
  private isUsingHTML5Fallback: boolean = false
  private errorHistory: AudioErrorInfo[] = []
  private maxErrorHistory: number = 50
  private retryAttempts: Map<string, number> = new Map()
  private html5AudioElements: Map<string, HTMLAudioElement> = new Map()
  private errorCallbacks: Array<(error: AudioErrorInfo) => void> = []
  private defaultSounds: Map<string, string> = new Map()

  /**
   * 创建音频错误处理器
   * @param options - 配置选项
   */
  constructor(options: AudioFallbackOptions = {}) {
    this.options = {
      enableSilentMode: options.enableSilentMode ?? true,
      enableHTML5AudioFallback: options.enableHTML5AudioFallback ?? true,
      showAudioWarning: options.showAudioWarning ?? true,
      maxRetryAttempts: options.maxRetryAttempts ?? 3,
      retryInterval: options.retryInterval ?? 1000
    }

    // 初始化默认音效映射
    this.initializeDefaultSounds()
  }

  /**
   * 初始化默认音效映射
   */
  private initializeDefaultSounds(): void {
    // 使用简单的音频数据URL作为默认音效
    const silentAudio = 'data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBSuBzvLZiTYIG2m98OScTgwOUarm7blmGgU7k9n1unEiBC13yO/eizEIHWq+8+OWT'
    
    this.defaultSounds.set('jump', silentAudio)
    this.defaultSounds.set('collect', silentAudio)
    this.defaultSounds.set('hit', silentAudio)
    this.defaultSounds.set('powerup', silentAudio)
  }

  /**
   * 处理AudioContext相关错误
   * @param error - 错误对象
   * @param audioContext - 出错的AudioContext
   * @returns 是否成功处理错误
   */
  handleAudioContextError(error: Error, audioContext?: AudioContext): boolean {
    const errorInfo: AudioErrorInfo = {
      type: 'context',
      message: `AudioContext错误: ${error.message}`,
      error,
      timestamp: Date.now(),
      context: {
        audioContextState: audioContext?.state
      }
    }

    this.logError(errorInfo)

    // 尝试恢复AudioContext
    if (audioContext) {
      try {
        if (audioContext.state === 'suspended') {
          audioContext.resume()
          console.log('成功恢复AudioContext')
          return true
        }
      } catch (resumeError) {
        console.error('恢复AudioContext失败:', resumeError)
      }
    }

    // 如果无法恢复，启用降级方案
    if (this.options.enableHTML5AudioFallback) {
      return this.fallbackToHTML5Audio()
    } else if (this.options.enableSilentMode) {
      return this.enableSilentMode()
    }

    return false
  }

  /**
   * 处理音频文件加载错误
   * @param error - 错误对象
   * @param soundName - 音效名称
   * @param url - 音频文件URL
   * @returns 是否成功处理错误
   */
  handleSoundLoadError(error: Error, soundName: string, url: string): boolean {
    const errorInfo: AudioErrorInfo = {
      type: 'loading',
      message: `音频加载错误: ${error.message}`,
      error,
      timestamp: Date.now(),
      context: { soundName, url }
    }

    this.logError(errorInfo)

    // 检查重试次数
    const currentAttempts = this.retryAttempts.get(soundName) || 0
    
    if (currentAttempts < this.options.maxRetryAttempts) {
      // 尝试重新加载
      this.retryAttempts.set(soundName, currentAttempts + 1)
      
      setTimeout(() => {
        this.retryLoadSound(soundName, url)
      }, this.options.retryInterval)
      
      return true
    }

    // 重试次数用完，使用默认音效
    console.warn(`音效 "${soundName}" 加载失败，使用默认音效`)
    return this.useDefaultSound(soundName)
  }

  /**
   * 处理音频播放错误
   * @param error - 错误对象
   * @param soundName - 音效名称
   * @returns 是否成功处理错误
   */
  handlePlaybackError(error: Error, soundName: string): boolean {
    const errorInfo: AudioErrorInfo = {
      type: 'playback',
      message: `音频播放错误: ${error.message}`,
      error,
      timestamp: Date.now(),
      context: { soundName }
    }

    this.logError(errorInfo)

    // 如果使用HTML5 Audio降级，尝试播放HTML5音频
    if (this.isUsingHTML5Fallback) {
      return this.playHTML5Audio(soundName)
    }

    // 否则静默处理
    return true
  }

  /**
   * 处理音频解码错误
   * @param error - 错误对象
   * @param soundName - 音效名称
   * @param url - 音频文件URL
   * @returns 是否成功处理错误
   */
  handleDecodingError(error: Error, soundName: string, url: string): boolean {
    const errorInfo: AudioErrorInfo = {
      type: 'decoding',
      message: `音频解码错误: ${error.message}`,
      error,
      timestamp: Date.now(),
      context: { soundName, url }
    }

    this.logError(errorInfo)

    // 尝试使用HTML5 Audio加载
    if (this.options.enableHTML5AudioFallback) {
      return this.loadHTML5Audio(soundName, url)
    }

    // 使用默认音效
    return this.useDefaultSound(soundName)
  }

  /**
   * 处理音频权限错误
   * @param error - 错误对象
   * @returns 是否成功处理错误
   */
  handlePermissionError(error: Error): boolean {
    const errorInfo: AudioErrorInfo = {
      type: 'permission',
      message: `音频权限错误: ${error.message}`,
      error,
      timestamp: Date.now()
    }

    this.logError(errorInfo)

    // 显示用户交互提示
    this.showUserInteractionPrompt()

    // 启用静默模式
    return this.enableSilentMode()
  }

  /**
   * 重试加载音频
   * @param soundName - 音效名称
   * @param url - 音频文件URL
   */
  private async retryLoadSound(soundName: string, url: string): Promise<void> {
    try {
      console.log(`重试加载音效: ${soundName}`)
      // 这里应该调用实际的音频加载逻辑
      // 由于我们在错误处理器中，这里只是示例
      console.log(`音效 "${soundName}" 重试加载成功`)
    } catch (retryError) {
      console.error(`音效 "${soundName}" 重试加载失败:`, retryError)
      this.useDefaultSound(soundName)
    }
  }

  /**
   * 使用默认音效
   * @param soundName - 音效名称
   * @returns 是否成功设置默认音效
   */
  private useDefaultSound(soundName: string): boolean {
    const defaultSound = this.defaultSounds.get(soundName)
    
    if (defaultSound) {
      console.log(`使用默认音效: ${soundName}`)
      return true
    }

    // 如果没有默认音效，使用通用的静默音效
    console.log(`使用静默音效: ${soundName}`)
    return true
  }

  /**
   * 降级到HTML5 Audio
   * @returns 是否成功启用HTML5 Audio降级
   */
  private fallbackToHTML5Audio(): boolean {
    if (this.isUsingHTML5Fallback) {
      return true
    }

    try {
      console.log('启用HTML5 Audio降级模式')
      this.isUsingHTML5Fallback = true

      // 显示降级警告
      if (this.options.showAudioWarning) {
        this.showAudioFallbackWarning('HTML5 Audio')
      }

      return true
    } catch (error) {
      console.error('启用HTML5 Audio降级失败:', error)
      return this.enableSilentMode()
    }
  }

  /**
   * 启用静默模式
   * @returns 是否成功启用静默模式
   */
  private enableSilentMode(): boolean {
    if (this.isInSilentMode) {
      return true
    }

    console.warn('启用音频静默模式')
    this.isInSilentMode = true

    // 显示静默模式警告
    if (this.options.showAudioWarning) {
      this.showAudioFallbackWarning('静默模式')
    }

    return true
  }

  /**
   * 加载HTML5 Audio
   * @param soundName - 音效名称
   * @param url - 音频文件URL
   * @returns 是否成功加载
   */
  private loadHTML5Audio(soundName: string, url: string): boolean {
    try {
      const audio = new Audio(url)
      audio.preload = 'auto'
      
      audio.addEventListener('canplaythrough', () => {
        console.log(`HTML5 Audio加载成功: ${soundName}`)
      })

      audio.addEventListener('error', (e) => {
        console.error(`HTML5 Audio加载失败: ${soundName}`, e)
        this.useDefaultSound(soundName)
      })

      this.html5AudioElements.set(soundName, audio)
      return true
    } catch (error) {
      console.error(`创建HTML5 Audio失败: ${soundName}`, error)
      return this.useDefaultSound(soundName)
    }
  }

  /**
   * 播放HTML5 Audio
   * @param soundName - 音效名称
   * @returns 是否成功播放
   */
  private playHTML5Audio(soundName: string): boolean {
    const audio = this.html5AudioElements.get(soundName)
    
    if (!audio) {
      console.warn(`HTML5 Audio未找到: ${soundName}`)
      return false
    }

    try {
      audio.currentTime = 0
      const playPromise = audio.play()
      
      if (playPromise) {
        playPromise.catch(error => {
          console.error(`HTML5 Audio播放失败: ${soundName}`, error)
        })
      }
      
      return true
    } catch (error) {
      console.error(`HTML5 Audio播放错误: ${soundName}`, error)
      return false
    }
  }

  /**
   * 显示音频降级警告
   * @param mode - 降级模式名称
   */
  private showAudioFallbackWarning(mode: string): void {
    const warning = document.createElement('div')
    warning.className = 'audio-fallback-warning'
    warning.style.cssText = `
      position: fixed;
      top: 50px;
      right: 20px;
      background: rgba(255, 193, 7, 0.9);
      color: #333;
      padding: 10px 15px;
      border-radius: 6px;
      font-size: 14px;
      z-index: 1001;
      box-shadow: 0 2px 8px rgba(0,0,0,0.2);
      animation: slideInRight 0.3s ease-out;
    `
    warning.innerHTML = `
      <strong>音频提示</strong><br>
      已启用${mode}
    `

    // 添加CSS动画
    const style = document.createElement('style')
    style.textContent = `
      @keyframes slideInRight {
        from { transform: translateX(100%); opacity: 0; }
        to { transform: translateX(0); opacity: 1; }
      }
    `
    document.head.appendChild(style)

    document.body.appendChild(warning)

    // 5秒后移除警告
    setTimeout(() => {
      warning.remove()
      style.remove()
    }, 5000)
  }

  /**
   * 显示用户交互提示
   */
  private showUserInteractionPrompt(): void {
    const prompt = document.createElement('div')
    prompt.className = 'audio-interaction-prompt'
    prompt.style.cssText = `
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(0, 0, 0, 0.8);
      color: white;
      padding: 20px;
      border-radius: 8px;
      text-align: center;
      z-index: 1002;
      font-size: 16px;
    `
    prompt.innerHTML = `
      <p>需要用户交互才能播放音频</p>
      <button id="enable-audio-btn" style="
        background: #007bff;
        color: white;
        border: none;
        padding: 10px 20px;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
      ">启用音频</button>
    `

    document.body.appendChild(prompt)

    // 添加按钮事件
    const enableBtn = prompt.querySelector('#enable-audio-btn') as HTMLButtonElement
    enableBtn.addEventListener('click', () => {
      prompt.remove()
      // 这里可以触发音频初始化
      console.log('用户已同意启用音频')
    })

    // 10秒后自动移除
    setTimeout(() => {
      if (prompt.parentNode) {
        prompt.remove()
      }
    }, 10000)
  }

  /**
   * 记录错误信息
   * @param errorInfo - 错误信息
   */
  private logError(errorInfo: AudioErrorInfo): void {
    this.errorHistory.push(errorInfo)
    
    // 限制错误历史记录数量
    if (this.errorHistory.length > this.maxErrorHistory) {
      this.errorHistory.shift()
    }

    // 触发错误回调
    this.errorCallbacks.forEach(callback => {
      try {
        callback(errorInfo)
      } catch (callbackError) {
        console.error('音频错误回调执行失败:', callbackError)
      }
    })

    console.error(`[AudioErrorHandler] ${errorInfo.message}`, errorInfo.error)
  }

  /**
   * 添加错误回调函数
   * @param callback - 错误回调函数
   */
  addErrorCallback(callback: (error: AudioErrorInfo) => void): void {
    this.errorCallbacks.push(callback)
  }

  /**
   * 移除错误回调函数
   * @param callback - 要移除的回调函数
   */
  removeErrorCallback(callback: (error: AudioErrorInfo) => void): void {
    const index = this.errorCallbacks.indexOf(callback)
    if (index > -1) {
      this.errorCallbacks.splice(index, 1)
    }
  }

  /**
   * 检查是否处于静默模式
   * @returns 是否处于静默模式
   */
  isSilentMode(): boolean {
    return this.isInSilentMode
  }

  /**
   * 检查是否使用HTML5 Audio降级
   * @returns 是否使用HTML5 Audio降级
   */
  isUsingHTML5FallbackMode(): boolean {
    return this.isUsingHTML5Fallback
  }

  /**
   * 获取错误历史记录
   * @returns 错误历史记录数组
   */
  getErrorHistory(): AudioErrorInfo[] {
    return [...this.errorHistory]
  }

  /**
   * 获取错误统计信息
   * @returns 错误统计对象
   */
  getErrorStats(): {
    totalErrors: number
    errorsByType: Record<string, number>
    recentErrors: number
    isInSilentMode: boolean
    isUsingHTML5Fallback: boolean
  } {
    const errorsByType: Record<string, number> = {}
    const recentThreshold = Date.now() - 60000 // 最近1分钟

    let recentErrors = 0

    this.errorHistory.forEach(error => {
      errorsByType[error.type] = (errorsByType[error.type] || 0) + 1
      if (error.timestamp > recentThreshold) {
        recentErrors++
      }
    })

    return {
      totalErrors: this.errorHistory.length,
      errorsByType,
      recentErrors,
      isInSilentMode: this.isInSilentMode,
      isUsingHTML5Fallback: this.isUsingHTML5Fallback
    }
  }

  /**
   * 清除错误历史记录
   */
  clearErrorHistory(): void {
    this.errorHistory = []
    this.retryAttempts.clear()
  }

  /**
   * 重置音频状态
   * @returns 是否成功重置
   */
  resetAudioState(): boolean {
    try {
      this.isInSilentMode = false
      this.isUsingHTML5Fallback = false
      this.retryAttempts.clear()
      
      // 清理HTML5 Audio元素
      this.html5AudioElements.forEach(audio => {
        audio.pause()
        audio.src = ''
      })
      this.html5AudioElements.clear()

      console.log('音频状态已重置')
      return true
    } catch (error) {
      console.error('重置音频状态失败:', error)
      return false
    }
  }

  /**
   * 销毁音频错误处理器
   */
  destroy(): void {
    this.resetAudioState()
    this.errorCallbacks = []
    this.clearErrorHistory()
    this.defaultSounds.clear()
  }
}

/**
 * 全局音频错误处理器实例
 */
export const audioErrorHandler = new AudioErrorHandler()